using System;
using System.Runtime.InteropServices;

namespace PDUConverter
{
    /// <summary>
    /// For TP-SCTS and all other timestamps that look the same.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct SmsTimestamp : IComparable
    {
        private byte year;
        private byte month;
        private byte day;
        private byte hour;
        private byte minute;
        private byte second;
        private int timeZoneOffset;
        /// <summary>
        /// The value for an invalid or not present timestamp.
        /// </summary>
        public static SmsTimestamp None;
        /// <summary>
        /// Decodes the timestamp out of a PDU stream.
        /// </summary>
        /// <param name="pdu">The string to get the timestamp from.</param>
        /// <param name="index">The current position in the string</param>
        public SmsTimestamp(string pdu, ref int index)
        {
            string data = BcdWorker.GetBytesString(pdu, index, 7);
            index += 7;
            string str2 = BcdWorker.DecodeSemiOctets(data);
            this.year = byte.Parse(str2.Substring(0, 2));
            this.month = byte.Parse(str2.Substring(2, 2));
            this.day = byte.Parse(str2.Substring(4, 2));
            this.hour = byte.Parse(str2.Substring(6, 2));
            this.minute = byte.Parse(str2.Substring(8, 2));
            this.second = byte.Parse(str2.Substring(10, 2));
            string s = str2.Substring(12, 2);
            byte num = Calc.HexToInt(s)[0];
            if ((num & 0x80) > 0)
            {
                num = (byte) (num & 0x7f);
                this.timeZoneOffset = -num;
            }
            else
            {
                this.timeZoneOffset = int.Parse(s);
            }
        }

        /// <summary>
        /// Creates the timestamp using a DateTime object and an offset.
        /// </summary>
        /// <param name="timestamp">The timestamp to initialize this object with.</param>
        /// <param name="timeZoneOffset">The time zone offset for the specified timestamp.</param>
        public SmsTimestamp(DateTime timestamp, int timeZoneOffset)
        {
            this.year = (byte) int.Parse(timestamp.Year.ToString().Substring(2, 2));
            this.month = (byte) timestamp.Month;
            this.day = (byte) timestamp.Day;
            this.hour = (byte) timestamp.Hour;
            this.minute = (byte) timestamp.Minute;
            this.second = (byte) timestamp.Second;
            this.timeZoneOffset = timeZoneOffset;
        }

        /// <summary>
        /// Gets the year.
        /// </summary>
        public byte Year
        {
            get
            {
                return this.year;
            }
        }
        /// <summary>
        /// Gets the month.
        /// </summary>
        public byte Month
        {
            get
            {
                return this.month;
            }
        }
        /// <summary>
        /// Gets the day.
        /// </summary>
        public byte Day
        {
            get
            {
                return this.day;
            }
        }
        /// <summary>
        /// Gets the hour.
        /// </summary>
        public byte Hour
        {
            get
            {
                return this.hour;
            }
        }
        /// <summary>
        /// Gets the minute.
        /// </summary>
        public byte Minute
        {
            get
            {
                return this.minute;
            }
        }
        /// <summary>
        /// Gets the second.
        /// </summary>
        public byte Second
        {
            get
            {
                return this.second;
            }
        }
        /// <summary>
        /// Gets the time zone offset as an integer.
        /// </summary>
        /// <remarks>One unit of this offset equals 15 minutes. If you don't need this actual value,
        /// consider using <see cref="P:GsmComm.PduConverter.SmsTimestamp.TimeZoneOffsetSpan" /> instead.</remarks>
        public int TimeZoneOffset
        {
            get
            {
                return this.timeZoneOffset;
            }
        }
        /// <summary>
        /// Gets the time zone offset as a string.
        /// </summary>
        public string TimeZoneOffsetString
        {
            get
            {
                TimeSpan timeZoneOffsetSpan = this.TimeZoneOffsetSpan;
                string str = (timeZoneOffsetSpan.Ticks < 0L) ? "-" : "+";
                string str2 = Math.Abs(timeZoneOffsetSpan.Hours).ToString().PadLeft(2, '0');
                string str3 = Math.Abs(timeZoneOffsetSpan.Minutes).ToString().PadLeft(2, '0');
                return (str + str2 + str3);
            }
        }
        /// <summary>
        /// Gets the time zone offset as a time span.
        /// </summary>
        public TimeSpan TimeZoneOffsetSpan
        {
            get
            {
                return TimeSpan.FromMinutes((double) (this.timeZoneOffset * 15));
            }
        }
        /// <summary>
        /// Returns the formatted date using DateTime of the current culture + the time zone offset.
        /// </summary>
        /// <returns>The formatted date string.</returns>
        /// <remarks>
        /// The returned string is useful for display but not for persistence because is uses
        /// the currently active culture to format the string, which can cause problems when trying to parse
        /// the persisted string. If you want to persist the timestamp, consider using
        /// <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToSortableString" /> instead.
        /// </remarks>
        public override string ToString()
        {
            return this.ToString(true);
        }

        /// <summary>
        /// Returns a formatted date string using DateTime of the current culture.
        /// </summary>
        /// <param name="includeTimeZoneOffset">Specify true to include the
        /// time zone offset in the string, false if it should not be included.</param>
        /// <returns>The formatted date string.</returns>
        /// <remarks>
        /// The returned string is useful for display but not for persistence because is uses
        /// the currently active culture to format the string, which can cause problems when trying to parse
        /// the persisted string. If you want to persist the timestamp, consider using
        /// <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToSortableString" /> instead.
        /// </remarks>
        public string ToString(bool includeTimeZoneOffset)
        {
            return (this.ToDateTime().ToString() + (includeTimeZoneOffset ? (" " + this.TimeZoneOffsetString) : ""));
        }

        /// <summary>
        /// Gets the time zone offset as a string useful to append to the sortable date string.
        /// </summary>
        private string TimeZoneOffsetSortableString
        {
            get
            {
                TimeSpan timeZoneOffsetSpan = this.TimeZoneOffsetSpan;
                string str = (timeZoneOffsetSpan.Ticks < 0L) ? "-" : "+";
                string str2 = Math.Abs(timeZoneOffsetSpan.Hours).ToString().PadLeft(2, '0');
                string str3 = Math.Abs(timeZoneOffsetSpan.Minutes).ToString().PadLeft(2, '0');
                return (str + str2 + ":" + str3);
            }
        }
        /// <summary>
        /// Returns a formatted date using the sortable date/time pattern and the time zone.
        /// </summary>
        /// <returns>The formatted date string.</returns>
        /// <remarks>The format is independent of the currently active culture and
        /// is always "yyyy-MM-ddTHH:mm:sszzz". It is useful for persisting the timestamp value as text.
        /// If you just want to display the value, consider using the <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToString" /> or
        /// <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToString(System.Boolean)" /> methods instead.</remarks>
        public string ToSortableString()
        {
            return (this.ToDateTime().ToString("s") + this.TimeZoneOffsetSortableString);
        }

        /// <summary>
        /// Returns the timestamp as a <see cref="T:System.DateTime" /> object.
        /// </summary>
        /// <returns>The converted date object.</returns>
        /// <remarks>
        /// <para>The <see cref="T:System.DateTime" /> object does not hold the time zone offset,
        /// this information will be lost when using this method. Use the
        /// TimeZoneOffset-releated properties and methods for working with the
        /// offset. If you just need the string representation, consider using the <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToString" />,
        /// <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToString(System.Boolean)" /> or <see cref="M:GsmComm.PduConverter.SmsTimestamp.ToSortableString" /> methods instead.
        /// </para>
        /// <para>
        /// An <see cref="T:GsmComm.PduConverter.SmsTimestamp" /> saves its years only with two digits by design, <see cref="T:System.DateTime" />
        /// uses 4 digits. The following conversion is performed when converting from an
        /// <see cref="T:GsmComm.PduConverter.SmsTimestamp" /> to a <see cref="T:System.DateTime" />: If the year is equal or greater than 90,
        /// then 1900 is added, otherwise 2000.
        /// </para>
        /// <para>
        /// If there is an error during conversion, the constant <see cref="F:System.DateTime.MinValue" />
        /// is returned, no exception is thrown.
        /// </para>
        /// </remarks>
        public DateTime ToDateTime()
        {
            try
            {
                return new DateTime((this.year >= 90) ? (0x76c + this.year) : (0x7d0 + this.year), this.month, this.day, this.hour, this.minute, this.second);
            }
            catch (ArgumentOutOfRangeException)
            {
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// Compares this instance to a specified object and returns an indication of their relative values.
        /// </summary>
        /// <param name="value">An object to compare, or a null reference</param>
        /// <returns>
        /// <list type="bullet">
        /// <item>less than zero = the instance is less than value.</item>
        /// <item>zero = the instance is equal to value.</item>
        /// <item>greater than zero = The instance is grater than value -or- value is a null reference.</item>
        /// </list>
        /// </returns>
        /// <exception cref="T:System.ArgumentException">value is not an <see cref="T:GsmComm.PduConverter.SmsTimestamp" />.</exception>
        public int CompareTo(object value)
        {
            if (value is SmsTimestamp)
            {
                SmsTimestamp timestamp = (SmsTimestamp) value;
                int num = 0;
                num = this.year.CompareTo(timestamp.Year);
                if (num != 0)
                {
                    return num;
                }
                num = this.month.CompareTo(timestamp.Month);
                if (num != 0)
                {
                    return num;
                }
                num = this.day.CompareTo(timestamp.Day);
                if (num != 0)
                {
                    return num;
                }
                num = this.hour.CompareTo(timestamp.Hour);
                if (num != 0)
                {
                    return num;
                }
                num = this.minute.CompareTo(timestamp.Minute);
                if (num != 0)
                {
                    return num;
                }
                num = this.second.CompareTo(timestamp.Second);
                if (num != 0)
                {
                    return num;
                }
                return this.timeZoneOffset.CompareTo(timestamp.TimeZoneOffset);
            }
            if (value != null)
            {
                throw new ArgumentException("value is not an SmsTimestamp.");
            }
            return 1;
        }

        static SmsTimestamp()
        {
            None = new SmsTimestamp(new DateTime(0x76c, 1, 1), 0);
        }
    }
}